/*************************************************************************
 *                                                                       *
 * This file is part of the DogosFrame distribution.  For full copyright *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 *                                                                       *
 *************************************************************************/

#ifndef DOGOS_include_COMMON_tbox_ParallelBuffer_h
#define DOGOS_include_COMMON_tbox_ParallelBuffer_h

#include <iostream>
#include <string>

namespace DOGOS
{

/**
 * Class ParallelBuffer is a simple I/O stream utility that
 * intercepts output from an ostream and redirects the output as necessary
 * for parallel I/O.  This class defines a stream buffer class for an
 * ostream class.
 */

class ParallelBuffer : public std::streambuf
{
public:

    /**
     * Create a parallel buffer class.  The object will require further
     * initialization to set up the I/O streams and prefix string.
     */
    ParallelBuffer();

    /**
     * The destructor simply deallocates any internal data
     * buffers.  It does not modify the output streams.
     */
    ~ParallelBuffer();

public:

    /**
     * Set the prefix that begins every new line to the output stream.
     * A sample prefix is "P=XXXXX: ", where XXXXX represents the node
     * number.
     */
    inline void
    set_prefix_string(
        const std::string& text);

    /**
     * Set the primary output stream.  If not NULL, then output data is
     * sent to this stream.  The primary output stream is typically stderr
     * or stdout or perhaps a log file.
     */
    inline void
    set_output_stream1(
        std::ostream* stream);

    /**
     * Set the secondary output stream.  If not NULL, then output data is sent
     * to this stream.  The secondary output stream is typically NULL or a log
     * file that mirrors the primary output stream.
     */
    inline void
    set_output_stream2(
        std::ostream* stream);

    /**
     * Write a text string to the output stream.  Note that the string is
     * not actually written until an end-of-line is detected.
     */
    inline void
    output_string(
        const std::string& text);

#ifdef _MSC_VER

    /**
     * Read an overflow character from the parallel buffer (called from
     * streambuf).  This is not implemented.  It is needed by the
     * MSVC++ stream implementation.
     */
    inline int
    underflow();
#endif

public:

    /**
     * Set whether the output stream will be active.  If the parallel buffer
     * stream is disabled, then no data is forwarded to the output streams.
     * The internal data buffer is deallocated and pointers are reset
     * whenever the parallel buffer is deactivated.
     */
    void
    set_active(
        bool active);

    /**
     * Write a text string of the specified length to the output file.  Note
     * that the string is not actually written until an end-of-line is detected.
     */
    void
    output_string(
        const std::string& text,
        const int length);

    /**
     * Synchronize the parallel buffer (called from streambuf).
     */
    int
    sync();

#if !defined(__INTEL_COMPILER) && (defined(__GNUG__))
    /**
     * Write the specified number of characters into the output stream (called
     * from streambuf).
     */
    std::streamsize
    xsputn(
        const std::string& text,
        std::streamsize n);
#endif

    /**
     * Write an overflow character into the parallel buffer (called from
     * streambuf).
     */
    int
    overflow(
        int ch);

private:

    void
    copy_to_buffer(
        const std::string& text,
        const int length);

    void
    output_buffer();              // output internal buffer data to streams

private:

    bool d_active;               // whether this output stream is active
    std::string d_prefix;        // string prefix to prepend output strings
    std::ostream* d_ostream1;    // primary output stream for buffer
    std::ostream* d_ostream2;    // secondary output stream (e.g., for log file)
    char* d_buffer;              // internal buffer to store accumulated string
    int d_buffer_size;           // size of the internal output buffer
    int d_buffer_ptr;            // number of charcters in the output buffer

    static const int DEFAULT_BUFFER_SIZE;
};

}

#include "COMMON/tbox/ParallelBuffer.inl"

#endif
